package top.jacktgq.imooc.candychat;

import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.DefaultChannelGroup;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import io.netty.util.concurrent.GlobalEventExecutor;
import top.jacktgq.utils.JsonUtils;
import top.jacktgq.utils.LogUtils;

import java.util.HashMap;
/**
 * @Title: ChatHandler.java 
 * @Package top.jacktgq.imooc.groupchat 
 * @Description: 处理WebSocket消息的Handler
 *                  TextWebSocketFrame：在Netty中，是用于为WebSocket专门处理文本的对象，frame是消息的载体
 * @author CandyWall   
 * @date 2021年2月4日 下午10:19:57 
 * @version V1.0
 */
public class ChatHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> {
    // 用于记录和管理所有客户端的Channel
    private static ChannelGroup userGroup = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE); 
    private static HashMap<String, Channel> userIdChannelMap = new HashMap<>();
    
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame msg) throws Exception {
        // 获取用户的Channel
        Channel channel = ctx.channel();
        
        // 1.获取客户端传输过来的消息
        String content = msg.text();
        System.out.println("接收到的数据：" + content);
        // 2.判断消息类型，根据不同的类型，处理不同的业务
        DataContent dataContent = JsonUtils.json2pojo(content, DataContent.class);
        /*switch(dataContent.getAction()) {
            case MsgActionEnum.CONNECT.type:
                break;
        }*/
        // 2.1 当Websocket第一次open的时候初始化Channel，把用户的Channel和userId关联起来
        if(dataContent.getAction() == MsgActionEnum.CONNECT.type) {
            String senderId = dataContent.getChatMsg().getSenderId();
            userIdChannelMap.put(senderId, channel);
        }
        // 2.2 聊天类型的消息，把聊天记录保存到数据库，同时标记消息的签收状态[默认未签收]
        else if(dataContent.getAction() == MsgActionEnum.CHAT.type) {
            ChatMsg chatMsg = dataContent.getChatMsg();
            // 保存消息到数据库
        }
        // 2.3 签收类型的消息，这是针对具体的消息进行签收，修改数据库中对应消息的签收状态
        else if(dataContent.getAction() == MsgActionEnum.SIGNED.type) {
            
        }
        // 2.4 心跳类型的消息
        else if(dataContent.getAction() == MsgActionEnum.KEEPALIVED.type) {
            
        }
        TextWebSocketFrame responseMsg = new TextWebSocketFrame("服务器时间：" + LogUtils.getCurrentTime() + " " + msg.text());
        // 只能flush一次
        /*for(Channel channel : channelGroup) {
            channel.writeAndFlush(responseMsg);
        }*/
        userGroup.writeAndFlush(responseMsg);
    }
    
    //当web客户端连接后，就会触发方法
    @Override
    public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
        System.out.println("有新的WebSocket客户端接入：LongText：" + ctx.channel().id().asLongText() + "，ShortText：" + ctx.channel().id().asShortText());
        // 获取客户端的Channel，并且放到ChannelGroup中进行管理
        userGroup.add(ctx.channel());
    }

    //当web客户端断开连接后，就会触发该方法
    @Override
    public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
        System.out.println("WebSocket客户端断开：长ID：" + ctx.channel().id().asLongText() + "短ID：" + ctx.channel().id().asShortText());
        // channelGroup.remove(ctx.channel());
        // 这里不需要自己去把当前的channel从channelGroup中移除，netty内部已经实现
        // 当触发handlerRemoved，ChannelGroup内部会自动移除对应客户端的channel
        System.out.println("Before Remove channelGroup.size() = " + userGroup.size());
        // channelGroup.remove(ctx.channel());
        // System.out.println("After Remove channelGroup.size() = " + channelGroup.size());
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        cause.printStackTrace();
        // 发生异常之后关闭连接，随后从ChannelGroup中移除
        ctx.channel().close();
        userGroup.remove(ctx);
    }
    
}
